ডেটা ফেচিং-এর জন্য রিঅ্যাক্ট সাসপেন্স আয়ত্ত করুন। ডিক্লারেটিভভাবে লোডিং স্টেট পরিচালনা করতে, ট্রানজিশনের মাধ্যমে UX উন্নত করতে এবং এরর বাউন্ডারির সাহায্যে এরর হ্যান্ডেল করতে শিখুন।
রিঅ্যাক্ট সাসপেন্স বাউন্ডারি: ডিক্লারেটিভ লোডিং স্টেট ম্যানেজমেন্টের একটি গভীর আলোচনা
আধুনিক ওয়েব ডেভেলপমেন্টের জগতে, একটি মসৃণ এবং প্রতিক্রিয়াশীল ব্যবহারকারীর অভিজ্ঞতা তৈরি করা সবচেয়ে গুরুত্বপূর্ণ। ডেভেলপারদের একটি সবচেয়ে বড় চ্যালেঞ্জ হলো লোডিং স্টেট পরিচালনা করা। ব্যবহারকারীর প্রোফাইলের জন্য ডেটা আনা থেকে শুরু করে অ্যাপ্লিকেশনের একটি নতুন বিভাগ লোড করা পর্যন্ত, অপেক্ষার মুহূর্তগুলো অত্যন্ত গুরুত্বপূর্ণ। ঐতিহাসিকভাবে, এর জন্য isLoading
, isFetching
, এবং hasError
এর মতো বুলিয়ান ফ্ল্যাগ ব্যবহার করা হতো, যা আমাদের কম্পোনেন্টগুলোতে ছড়িয়ে ছিটিয়ে থাকতো। এই ইম্পেরেটিভ পদ্ধতি আমাদের কোডকে অগোছালো করে, লজিককে জটিল করে এবং প্রায়শই রেস কন্ডিশনের মতো বাগের উৎস হয়ে দাঁড়ায়।
এখানেই আসে রিঅ্যাক্ট সাসপেন্স। প্রথমে React.lazy()
এর সাথে কোড-স্প্লিটিং এর জন্য পরিচিত হলেও, রিঅ্যাক্ট ১৮ এর সাথে এর ক্ষমতা নাটকীয়ভাবে প্রসারিত হয়েছে এবং এটি অ্যাসিঙ্ক্রোনাস অপারেশন, বিশেষ করে ডেটা ফেচিং পরিচালনা করার জন্য একটি শক্তিশালী, প্রথম-শ্রেণীর মেকানিজম হয়ে উঠেছে। সাসপেন্স আমাদের ডিক্লারেটিভ উপায়ে লোডিং স্টেট পরিচালনা করতে দেয়, যা আমাদের কম্পোনেন্ট লেখার এবং সে সম্পর্কে চিন্তা করার পদ্ধতিকে মৌলিকভাবে পরিবর্তন করে। "আমি কি লোড হচ্ছি?" জিজ্ঞাসা করার পরিবর্তে, আমাদের কম্পোনেন্টগুলো সহজভাবে বলতে পারে, "আমার রেন্ডার করার জন্য এই ডেটা প্রয়োজন। আমি অপেক্ষা করার সময়, অনুগ্রহ করে এই ফলব্যাক UI দেখান।"
এই বিস্তারিত গাইডটি আপনাকে স্টেট ম্যানেজমেন্টের ঐতিহ্যগত পদ্ধতি থেকে রিঅ্যাক্ট সাসপেন্সের ডিক্লারেটিভ প্যারাডাইমে নিয়ে যাবে। আমরা সাসপেন্স বাউন্ডারি কী, কোড-স্প্লিটিং এবং ডেটা ফেচিং উভয়ের জন্য কীভাবে কাজ করে, এবং কীভাবে জটিল লোডিং UI তৈরি করে ব্যবহারকারীদের হতাশ না করে আনন্দিত করা যায়, তা অন্বেষণ করব।
পুরানো পদ্ধতি: ম্যানুয়াল লোডিং স্টেটের ঝক্কি
সাসপেন্সের সৌন্দর্য পুরোপুরি উপলব্ধি করার আগে, এটি কোন সমস্যার সমাধান করে তা বোঝা অপরিহার্য। আসুন একটি সাধারণ কম্পোনেন্ট দেখি যা useEffect
এবং useState
হুক ব্যবহার করে ডেটা ফেচ করে।
ভাবুন একটি কম্পোনেন্টের কথা, যা ব্যবহারকারীর ডেটা ফেচ এবং প্রদর্শন করতে চায়:
import React, { useState, useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// নতুন userId এর জন্য স্টেট রিসেট করুন
setIsLoading(true);
setUser(null);
setError(null);
const fetchUser = async () => {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
setUser(data);
} catch (err) {
setError(err);
} finally {
setIsLoading(false);
}
};
fetchUser();
}, [userId]); // userId পরিবর্তন হলে পুনরায় ফেচ করুন
if (isLoading) {
return <p>Loading profile...</p>;
}
if (error) {
return <p>Error: {error.message}</p>;
}
return (
<div>
<h1>{user.name}</h1>
<p>Email: {user.email}</p>
</div>
);
}
এই প্যাটার্নটি কার্যকরী, কিন্তু এর বেশ কিছু অসুবিধা আছে:
- বয়লারপ্লেট: প্রতিটি অ্যাসিঙ্ক্রোনাস অপারেশনের জন্য আমাদের কমপক্ষে তিনটি স্টেট ভেরিয়েবল (
data
,isLoading
,error
) প্রয়োজন। একটি জটিল অ্যাপ্লিকেশনে এটি ভালোভাবে স্কেল করে না। - ছড়িয়ে ছিটিয়ে থাকা লজিক: রেন্ডারিং লজিকটি কন্ডিশনাল চেক (
if (isLoading)
,if (error)
) দিয়ে বিভক্ত থাকে। প্রধান "হ্যাপি পাথ" রেন্ডার লজিকটি একেবারে নীচে ঠেলে দেওয়া হয়, যা কম্পোনেন্ট পড়া কঠিন করে তোলে। - রেস কন্ডিশন:
useEffect
হুকের জন্য সতর্কতার সাথে ডিপেন্ডেন্সি ম্যানেজমেন্ট প্রয়োজন। সঠিক ক্লিনআপ ছাড়া, যদিuserId
প্রপ দ্রুত পরিবর্তিত হয় তবে একটি দ্রুত প্রতিক্রিয়া একটি ধীর প্রতিক্রিয়া দ্বারা ওভাররাইট হয়ে যেতে পারে। যদিও আমাদের উদাহরণটি সহজ, জটিল পরিস্থিতিতে সহজেই সূক্ষ্ম বাগ তৈরি হতে পারে। - ওয়াটারফল ফেচ: যদি একটি চাইল্ড কম্পোনেন্টকেও ডেটা ফেচ করতে হয়, তবে প্যারেন্ট লোডিং শেষ না করা পর্যন্ত এটি রেন্ডারিং (এবং ফেচিং) শুরু করতে পারে না। এটি অদক্ষ ডেটা-লোডিং ওয়াটারফলের দিকে পরিচালিত করে।
রিঅ্যাক্ট সাসপেন্সের আগমন: একটি ধারণাগত পরিবর্তন
সাসপেন্স এই মডেলটিকে পুরোপুরি উল্টে দেয়। কম্পোনেন্ট অভ্যন্তরীণভাবে লোডিং স্টেট পরিচালনা করার পরিবর্তে, এটি একটি অ্যাসিঙ্ক্রোনাস অপারেশনের উপর তার নির্ভরতা সরাসরি রিঅ্যাক্টকে জানায়। যদি তার প্রয়োজনীয় ডেটা এখনও উপলব্ধ না থাকে, তাহলে কম্পোনেন্ট রেন্ডারিং "সাসপেন্ড" করে।
যখন একটি কম্পোনেন্ট সাসপেন্ড করে, রিঅ্যাক্ট কম্পোনেন্ট ট্রি-এর উপরে গিয়ে নিকটতম সাসপেন্স বাউন্ডারি খুঁজে বের করে। একটি সাসপেন্স বাউন্ডারি হলো একটি কম্পোনেন্ট যা আপনি আপনার ট্রি-তে <Suspense>
ব্যবহার করে সংজ্ঞায়িত করেন। এই বাউন্ডারিটি তখন একটি ফলব্যাক UI (যেমন একটি স্পিনার বা একটি স্কেলেটন লোডার) রেন্ডার করবে যতক্ষণ না এর মধ্যে থাকা সমস্ত কম্পোনেন্ট তাদের ডেটা নির্ভরতা সমাধান করে।
মূল ধারণাটি হলো ডেটা নির্ভরতাকে সেই কম্পোনেন্টের সাথে কো-লোকেট করা যার এটি প্রয়োজন, এবং লোডিং UI-কে কম্পোনেন্ট ট্রি-এর উচ্চ স্তরে কেন্দ্রীভূত করা। এটি কম্পোনেন্ট লজিককে পরিষ্কার করে এবং ব্যবহারকারীর লোডিং অভিজ্ঞতার উপর আপনাকে শক্তিশালী নিয়ন্ত্রণ দেয়।
একটি কম্পোনেন্ট কীভাবে "সাসপেন্ড" করে?
সাসপেন্সের পেছনের জাদুটি এমন একটি প্যাটার্নের মধ্যে নিহিত যা প্রথমে অস্বাভাবিক মনে হতে পারে: একটি Promise থ্রো করা। একটি সাসপেন্স-সক্ষম ডেটা উৎস এভাবে কাজ করে:
- যখন একটি কম্পোনেন্ট ডেটার জন্য জিজ্ঞাসা করে, ডেটা উৎসটি পরীক্ষা করে দেখে যে এটি ডেটা ক্যাশ করেছে কিনা।
- যদি ডেটা উপলব্ধ থাকে, তবে এটি সিঙ্ক্রোনাসভাবে তা ফেরত দেয়।
- যদি ডেটা উপলব্ধ না থাকে (অর্থাৎ, এটি বর্তমানে ফেচ করা হচ্ছে), ডেটা উৎসটি চলমান ফেচ অনুরোধকে প্রতিনিধিত্বকারী Promise-টি থ্রো করে।
রিঅ্যাক্ট এই থ্রো করা Promise-টি ধরে ফেলে। এটি আপনার অ্যাপ ক্র্যাশ করে না। পরিবর্তে, এটি এটিকে একটি সংকেত হিসাবে ব্যাখ্যা করে: "এই কম্পোনেন্টটি এখনও রেন্ডার করার জন্য প্রস্তুত নয়। এটিকে থামান, এবং একটি ফলব্যাক দেখানোর জন্য এর উপরে একটি সাসপেন্স বাউন্ডারি খুঁজুন।" একবার Promise-টি সমাধান হয়ে গেলে, রিঅ্যাক্ট কম্পোনেন্টটি পুনরায় রেন্ডার করার চেষ্টা করবে, যা এখন তার ডেটা পাবে এবং সফলভাবে রেন্ডার করবে।
<Suspense>
বাউন্ডারি: আপনার লোডিং UI ডিক্লারেটর
<Suspense>
কম্পোনেন্টটি এই প্যাটার্নের কেন্দ্রবিন্দু। এটি ব্যবহার করা অবিশ্বাস্যভাবে সহজ, একটি মাত্র প্রয়োজনীয় প্রপ নেয়: fallback
।
import { Suspense } from 'react';
function App() {
return (
<div>
<h1>My Application</h1>
<Suspense fallback={<p>Loading content...</p>}>
<SomeComponentThatFetchesData />
</Suspense>
</div>
);
}
এই উদাহরণে, যদি SomeComponentThatFetchesData
সাসপেন্ড করে, ব্যবহারকারী ডেটা প্রস্তুত না হওয়া পর্যন্ত "Loading content..." বার্তাটি দেখতে পাবে। ফলব্যাকটি যেকোনো বৈধ রিঅ্যাক্ট নোড হতে পারে, একটি সাধারণ স্ট্রিং থেকে একটি জটিল স্কেলেটন কম্পোনেন্ট পর্যন্ত।
ক্লাসিক ব্যবহার: React.lazy()
দিয়ে কোড স্প্লিটিং
সাসপেন্সের সবচেয়ে প্রতিষ্ঠিত ব্যবহার হলো কোড স্প্লিটিং। এটি আপনাকে একটি কম্পোনেন্টের জাভাস্ক্রিপ্ট লোড করা স্থগিত করতে দেয় যতক্ষণ না এটি সত্যিই প্রয়োজন হয়।
import React, { Suspense, lazy } from 'react';
// এই কম্পোনেন্টের কোড প্রাথমিক বান্ডেলে থাকবে না।
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<div>
<h2>Some content that loads immediately</h2>
<Suspense fallback={<div>Loading component...</div>}>
<HeavyComponent />
</Suspense>
</div>
);
}
এখানে, রিঅ্যাক্ট কেবল তখনই HeavyComponent
-এর জন্য জাভাস্ক্রিপ্ট ফেচ করবে যখন এটি প্রথমবার রেন্ডার করার চেষ্টা করবে। যখন এটি ফেচ এবং পার্স করা হচ্ছে, তখন সাসপেন্স ফলব্যাক প্রদর্শিত হবে। এটি প্রাথমিক পেজ লোডের সময় উন্নত করার জন্য একটি শক্তিশালী কৌশল।
আধুনিক ক্ষেত্র: সাসপেন্স দিয়ে ডেটা ফেচিং
যদিও রিঅ্যাক্ট সাসপেন্স মেকানিজম সরবরাহ করে, এটি একটি নির্দিষ্ট ডেটা-ফেচিং ক্লায়েন্ট সরবরাহ করে না। ডেটা ফেচিংয়ের জন্য সাসপেন্স ব্যবহার করতে, আপনার একটি ডেটা উৎস প্রয়োজন যা এর সাথে একীভূত হয় (অর্থাৎ, এমন একটি যা ডেটা পেন্ডিং থাকা অবস্থায় একটি Promise থ্রো করে)।
Relay এবং Next.js-এর মতো ফ্রেমওয়ার্কগুলোতে সাসপেন্সের জন্য বিল্ট-ইন, প্রথম-শ্রেণীর সমর্থন রয়েছে। TanStack Query (পূর্বে React Query) এবং SWR-এর মতো জনপ্রিয় ডেটা-ফেচিং লাইব্রেরিগুলোও এর জন্য পরীক্ষামূলক বা সম্পূর্ণ সমর্থন সরবরাহ করে।
ধারণাটি বোঝার জন্য, আসুন fetch
API-এর চারপাশে একটি খুব সাধারণ, ধারণাগত র্যাপার তৈরি করি যাতে এটি সাসপেন্স-সামঞ্জস্যপূর্ণ হয়। দ্রষ্টব্য: এটি শিক্ষামূলক উদ্দেশ্যে একটি সরলীকৃত উদাহরণ এবং প্রোডাকশন-রেডি নয়। এতে সঠিক ক্যাশিং এবং এরর হ্যান্ডলিংয়ের জটিলতা নেই।
// data-fetcher.js
// ফলাফল সংরক্ষণ করার জন্য একটি সাধারণ ক্যাশে
const cache = new Map();
export function fetchData(url) {
if (!cache.has(url)) {
cache.set(url, { status: 'pending', promise: fetchAndCache(url) });
}
const record = cache.get(url);
if (record.status === 'pending') {
throw record.promise; // এটাই জাদু!
}
if (record.status === 'error') {
throw record.error;
}
if (record.status === 'success') {
return record.data;
}
}
async function fetchAndCache(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Fetch failed with status ${response.status}`);
}
const data = await response.json();
cache.set(url, { status: 'success', data });
} catch (e) {
cache.set(url, { status: 'error', error: e });
}
}
এই র্যাপারটি প্রতিটি URL-এর জন্য একটি সাধারণ স্ট্যাটাস বজায় রাখে। যখন fetchData
কল করা হয়, এটি স্ট্যাটাস পরীক্ষা করে। যদি এটি পেন্ডিং থাকে, তবে এটি প্রমিসটি থ্রো করে। যদি এটি সফল হয়, তবে এটি ডেটা ফেরত দেয়। এখন, আসুন আমাদের UserProfile
কম্পোনেন্টটি এটি ব্যবহার করে পুনরায় লিখি।
// UserProfile.js
import React, { Suspense } from 'react';
import { fetchData } from './data-fetcher';
// যে কম্পোনেন্টটি আসলে ডেটা ব্যবহার করে
function ProfileDetails({ userId }) {
// ডেটা পড়ার চেষ্টা করুন। যদি এটি প্রস্তুত না হয়, তবে এটি সাসপেন্ড করবে।
const user = fetchData(`https://api.example.com/users/${userId}`);
return (
<div>
<h1>{user.name}</h1>
<p>Email: {user.email}</p>
</div>
);
}
// প্যারেন্ট কম্পোনেন্ট যা লোডিং স্টেট UI সংজ্ঞায়িত করে
export function UserProfile({ userId }) {
return (
<Suspense fallback={<p>Loading profile...</p>}>
<ProfileDetails userId={userId} />
</Suspense>
);
}
পার্থক্য দেখুন! ProfileDetails
কম্পোনেন্টটি পরিষ্কার এবং শুধুমাত্র ডেটা রেন্ডার করার উপর দৃষ্টি নিবদ্ধ করে। এতে কোনো isLoading
বা error
স্টেট নেই। এটি কেবল তার প্রয়োজনীয় ডেটা অনুরোধ করে। একটি লোডিং ইন্ডিকেটর দেখানোর দায়িত্বটি প্যারেন্ট কম্পোনেন্ট, UserProfile
-এ স্থানান্তরিত হয়েছে, যা অপেক্ষার সময় কী দেখাতে হবে তা ডিক্লারেটিভভাবে বলে দেয়।
জটিল লোডিং স্টেট পরিচালনা
সাসপেন্সের আসল শক্তি তখনই স্পষ্ট হয় যখন আপনি একাধিক অ্যাসিঙ্ক্রোনাস নির্ভরতা সহ জটিল UI তৈরি করেন।
ধাপে ধাপে UI দেখানোর জন্য নেস্টেড সাসপেন্স বাউন্ডারি
আপনি আরও পরিমার্জিত লোডিং অভিজ্ঞতা তৈরি করতে সাসপেন্স বাউন্ডারি নেস্ট করতে পারেন। একটি ড্যাশবোর্ড পেজের কথা ভাবুন যেখানে একটি সাইডবার, একটি প্রধান বিষয়বস্তু এলাকা এবং সাম্প্রতিক কার্যকলাপের একটি তালিকা রয়েছে। এগুলোর প্রত্যেকটির জন্য নিজস্ব ডেটা ফেচ প্রয়োজন হতে পারে।
function DashboardPage() {
return (
<div>
<h1>Dashboard</h1>
<div className="layout">
<Suspense fallback={<p>Loading navigation...</p>}>
<Sidebar />
</Suspense>
<main>
<Suspense fallback={<ProfileSkeleton />}>
<MainContent />
</Suspense>
<Suspense fallback={<ActivityFeedSkeleton />}>
<ActivityFeed />
</Suspense>
</main>
</div>
</div>
);
}
এই কাঠামোর সাথে:
Sidebar
তার ডেটা প্রস্তুত হওয়ার সাথে সাথে উপস্থিত হতে পারে, এমনকি যদি প্রধান বিষয়বস্তু এখনও লোড হচ্ছে।MainContent
এবংActivityFeed
স্বাধীনভাবে লোড হতে পারে। ব্যবহারকারী প্রতিটি বিভাগের জন্য একটি বিস্তারিত স্কেলেটন লোডার দেখতে পায়, যা একটি একক, পেজ-ব্যাপী স্পিনারের চেয়ে ভালো কনটেক্সট প্রদান করে।
এটি আপনাকে যত তাড়াতাড়ি সম্ভব ব্যবহারকারীর কাছে দরকারী বিষয়বস্তু দেখাতে দেয়, যা অনুভূত পারফরম্যান্সকে নাটকীয়ভাবে উন্নত করে।
UI "পপকর্নিং" এড়ানো
কখনও কখনও, ধাপে ধাপে পদ্ধতিটি একটি বিরক্তিকর প্রভাবের দিকে নিয়ে যেতে পারে যেখানে একাধিক স্পিনার দ্রুত পর্যায়ক্রমে উপস্থিত হয় এবং অদৃশ্য হয়ে যায়, একটি প্রভাবকে প্রায়শই "পপকর্নিং" বলা হয়। এই সমস্যা সমাধানের জন্য, আপনি সাসপেন্স বাউন্ডারিটি ট্রি-এর আরও উপরে নিয়ে যেতে পারেন।
function DashboardPage() {
return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<DashboardSkeleton />}>
<div className="layout">
<Sidebar />
<main>
<MainContent />
<ActivityFeed />
</main>
</div>
</Suspense>
</div>
);
}
এই সংস্করণে, একটি একক DashboardSkeleton
দেখানো হয় যতক্ষণ না সমস্ত চাইল্ড কম্পোনেন্ট (Sidebar
, MainContent
, ActivityFeed
) তাদের ডেটা প্রস্তুত করে। পুরো ড্যাশবোর্ডটি তখন একবারে উপস্থিত হয়। নেস্টেড বাউন্ডারি এবং একটি একক উচ্চ-স্তরের বাউন্ডারির মধ্যে পছন্দটি একটি UX ডিজাইনের সিদ্ধান্ত যা সাসপেন্স বাস্তবায়ন করা সহজ করে তোলে।
এরর বাউন্ডারির মাধ্যমে এরর হ্যান্ডলিং
সাসপেন্স একটি প্রমিসের পেন্ডিং স্টেট পরিচালনা করে, কিন্তু রিজেক্টেড স্টেটের কী হবে? যদি একটি কম্পোনেন্ট দ্বারা থ্রো করা প্রমিসটি রিজেক্ট হয় (যেমন, একটি নেটওয়ার্ক এরর), তবে এটি রিঅ্যাক্টে অন্য যেকোনো রেন্ডারিং এররের মতো আচরণ করবে।
সমাধান হলো এরর বাউন্ডারি ব্যবহার করা। একটি এরর বাউন্ডারি হলো একটি ক্লাস কম্পোনেন্ট যা একটি বিশেষ লাইফসাইকেল মেথড, componentDidCatch()
বা একটি স্ট্যাটিক মেথড getDerivedStateFromError()
সংজ্ঞায়িত করে। এটি তার চাইল্ড কম্পোনেন্ট ট্রি-এর যেকোনো জায়গায় জাভাস্ক্রিপ্ট এরর ধরে, সেই এররগুলো লগ করে এবং একটি ফলব্যাক UI প্রদর্শন করে।
এখানে একটি সহজ এরর বাউন্ডারি কম্পোনেন্ট রয়েছে:
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
// স্টেট আপডেট করুন যাতে পরবর্তী রেন্ডার ফলব্যাক UI দেখায়।
return { hasError: true, error: error };
}
componentDidCatch(error, errorInfo) {
// আপনি একটি এরর রিপোর্টিং সার্ভিসে এরর লগ করতে পারেন
console.error("Caught an error:", error, errorInfo);
}
render() {
if (this.state.hasError) {
// আপনি যেকোনো কাস্টম ফলব্যাক UI রেন্ডার করতে পারেন
return <h1>Something went wrong. Please try again.</h1>;
}
return this.props.children;
}
}
আপনি তখন এরর বাউন্ডারি এবং সাসপেন্সকে একত্রিত করে একটি শক্তিশালী সিস্টেম তৈরি করতে পারেন যা তিনটি স্টেটই পরিচালনা করে: পেন্ডিং, সাকসেস এবং এরর।
import { Suspense } from 'react';
import ErrorBoundary from './ErrorBoundary';
import { UserProfile } from './UserProfile';
function App() {
return (
<div>
<h2>User Information</h2>
<ErrorBoundary>
<Suspense fallback={<p>Loading...</p>}>
<UserProfile userId={123} />
</Suspense>
</ErrorBoundary>
</div>
);
}
এই প্যাটার্নের সাথে, যদি UserProfile
এর ভেতরের ডেটা ফেচ সফল হয়, প্রোফাইলটি দেখানো হয়। যদি এটি পেন্ডিং থাকে, সাসপেন্স ফলব্যাক দেখানো হয়। যদি এটি ব্যর্থ হয়, এরর বাউন্ডারির ফলব্যাক দেখানো হয়। লজিকটি ডিক্লারেটিভ, কম্পোজিশনাল এবং বোঝা সহজ।
ট্রানজিশন: নন-ব্লকিং UI আপডেটের চাবিকাঠি
এই ধাঁধার একটি চূড়ান্ত অংশ রয়েছে। একটি ব্যবহারকারীর ইন্টারঅ্যাকশনের কথা ভাবুন যা একটি নতুন ডেটা ফেচ ট্রিগার করে, যেমন একটি ভিন্ন ব্যবহারকারীর প্রোফাইল দেখার জন্য "Next" বোতামে ক্লিক করা। উপরের সেটআপের সাথে, যেই মুহূর্তে বোতামটি ক্লিক করা হয় এবং userId
প্রপ পরিবর্তিত হয়, UserProfile
কম্পোনেন্টটি আবার সাসপেন্ড করবে। এর মানে হলো বর্তমানে দৃশ্যমান প্রোফাইলটি অদৃশ্য হয়ে যাবে এবং লোডিং ফলব্যাক দ্বারা প্রতিস্থাপিত হবে। এটি হঠাৎ এবং বিঘ্নকারী মনে হতে পারে।
এখানেই ট্রানজিশন আসে। ট্রানজিশন হলো রিঅ্যাক্ট ১৮-এর একটি নতুন ফিচার যা আপনাকে কিছু স্টেট আপডেটকে নন-আর্জেন্ট হিসাবে চিহ্নিত করতে দেয়। যখন একটি স্টেট আপডেট একটি ট্রানজিশনে মোড়ানো হয়, রিঅ্যাক্ট পুরানো UI (পুরানো বিষয়বস্তু) প্রদর্শন করতে থাকবে যখন এটি পটভূমিতে নতুন বিষয়বস্তু প্রস্তুত করছে। এটি কেবল তখনই UI আপডেটটি কমিট করবে যখন নতুন বিষয়বস্তু প্রদর্শনের জন্য প্রস্তুত হবে।
এর জন্য প্রধান API হলো useTransition
হুক।
import React, { useState, useTransition, Suspense } from 'react';
import { UserProfile } from './UserProfile';
function ProfileSwitcher() {
const [userId, setUserId] = useState(1);
const [isPending, startTransition] = useTransition();
const handleNextClick = () => {
startTransition(() => {
setUserId(id => id + 1);
});
};
return (
<div>
<button onClick={handleNextClick} disabled={isPending}>
Next User
</button>
{isPending && <span> Loading new profile...</span>}
<ErrorBoundary>
<Suspense fallback={<p>Loading initial profile...</p>}>
<UserProfile userId={userId} />
</Suspense>
</ErrorBoundary>
</div>
);
}
এখন যা ঘটবে তা হলো:
userId: 1
এর জন্য প্রাথমিক প্রোফাইল লোড হয়, সাসপেন্স ফলব্যাক দেখিয়ে।- ব্যবহারকারী "Next User" এ ক্লিক করে।
setUserId
কলটিstartTransition
-এ মোড়ানো হয়।- রিঅ্যাক্ট মেমরিতে নতুন
userId
২ দিয়েUserProfile
রেন্ডার করা শুরু করে। এটি এটিকে সাসপেন্ড করতে বাধ্য করে। - গুরুত্বপূর্ণভাবে, সাসপেন্স ফলব্যাক দেখানোর পরিবর্তে, রিঅ্যাক্ট পুরানো UI (ব্যবহারকারী ১ এর প্রোফাইল) স্ক্রিনে রাখে।
useTransition
দ্বারা ফেরত দেওয়াisPending
বুলিয়ানটিtrue
হয়ে যায়, যা আমাদের পুরানো বিষয়বস্তু আনমাউন্ট না করে একটি সূক্ষ্ম, ইনলাইন লোডিং ইন্ডিকেটর দেখাতে দেয়।- একবার ব্যবহারকারী ২ এর ডেটা ফেচ হয়ে গেলে এবং
UserProfile
সফলভাবে রেন্ডার করতে পারলে, রিঅ্যাক্ট আপডেটটি কমিট করে, এবং নতুন প্রোফাইলটি মসৃণভাবে উপস্থিত হয়।
ট্রানজিশন নিয়ন্ত্রণের চূড়ান্ত স্তর সরবরাহ করে, যা আপনাকে অত্যাধুনিক এবং ব্যবহারকারী-বান্ধব লোডিং অভিজ্ঞতা তৈরি করতে সক্ষম করে যা কখনও বিরক্তিকর মনে হয় না।
সেরা অনুশীলন এবং বিশ্বব্যাপী বিবেচনা
- কৌশলগতভাবে বাউন্ডারি স্থাপন করুন: প্রতিটি ছোট কম্পোনেন্টকে সাসপেন্স বাউন্ডারিতে মোড়াবেন না। সেগুলোকে আপনার অ্যাপ্লিকেশনের যৌক্তিক পয়েন্টে রাখুন যেখানে একটি লোডিং স্টেট ব্যবহারকারীর কাছে অর্থবহ, যেমন একটি পেজ, একটি বড় প্যানেল, বা একটি গুরুত্বপূর্ণ উইজেট।
- অর্থবহ ফলব্যাক ডিজাইন করুন: জেনেরিক স্পিনারগুলো সহজ, কিন্তু স্কেলেটন লোডার যা লোড হওয়া বিষয়বস্তুর আকৃতির অনুকরণ করে, তা অনেক ভালো ব্যবহারকারীর অভিজ্ঞতা প্রদান করে। তারা লেআউট শিফট কমায় এবং ব্যবহারকারীকে কী বিষয়বস্তু আসবে তা অনুমান করতে সাহায্য করে।
- অ্যাক্সেসিবিলিটি বিবেচনা করুন: লোডিং স্টেট দেখানোর সময়, নিশ্চিত করুন যে সেগুলো অ্যাক্সেসিবল। স্ক্রিন রিডার ব্যবহারকারীদের জানাতে যে বিষয়বস্তু আপডেট হচ্ছে, কন্টেন্ট কন্টেইনারে
aria-busy="true"
এর মতো ARIA অ্যাট্রিবিউট ব্যবহার করুন। - সার্ভার কম্পোনেন্টকে আলিঙ্গন করুন: সাসপেন্স হলো রিঅ্যাক্ট সার্ভার কম্পোনেন্টস (RSC) এর জন্য একটি ভিত্তিগত প্রযুক্তি। Next.js-এর মতো ফ্রেমওয়ার্ক ব্যবহার করার সময়, সাসপেন্স আপনাকে ডেটা উপলব্ধ হওয়ার সাথে সাথে সার্ভার থেকে HTML স্ট্রিম করতে দেয়, যা বিশ্বব্যাপী দর্শকদের জন্য অবিশ্বাস্যভাবে দ্রুত প্রাথমিক পেজ লোডের দিকে পরিচালিত করে।
- ইকোসিস্টেমের সুবিধা নিন: যদিও অন্তর্নিহিত নীতিগুলো বোঝা গুরুত্বপূর্ণ, প্রোডাকশন অ্যাপ্লিকেশনগুলোর জন্য, TanStack Query, SWR, বা Relay-এর মতো পরীক্ষিত লাইব্রেরিগুলোর উপর নির্ভর করুন। তারা ক্যাশিং, ডিডুপ্লিকেশন এবং অন্যান্য জটিলতা পরিচালনা করে এবং সাথে সাথে মসৃণ সাসপেন্স ইন্টিগ্রেশন সরবরাহ করে।
উপসংহার
রিঅ্যাক্ট সাসপেন্স কেবল একটি নতুন ফিচারের চেয়েও বেশি কিছু; এটি রিঅ্যাক্ট অ্যাপ্লিকেশনগুলোতে আমরা কীভাবে অ্যাসিঙ্ক্রোনিসিটির সাথে কাজ করি তার একটি মৌলিক বিবর্তন। ম্যানুয়াল, ইম্পেরেটিভ লোডিং ফ্ল্যাগ থেকে সরে এসে এবং একটি ডিক্লারেটিভ মডেল গ্রহণ করে, আমরা এমন কম্পোনেন্ট লিখতে পারি যা আরও পরিষ্কার, আরও স্থিতিস্থাপক এবং কম্পোজ করা সহজ।
পেন্ডিং স্টেটের জন্য <Suspense>
, ব্যর্থতার স্টেটের জন্য এরর বাউন্ডারি এবং মসৃণ আপডেটের জন্য useTransition
একত্রিত করে, আপনার কাছে একটি সম্পূর্ণ এবং শক্তিশালী টুলকিট রয়েছে। আপনি সাধারণ লোডিং স্পিনার থেকে শুরু করে জটিল, ধাপে ধাপে ড্যাশবোর্ড প্রকাশের সবকিছুই ন্যূনতম, অনুমানযোগ্য কোড দিয়ে করতে পারেন। আপনি যখন আপনার প্রকল্পগুলোতে সাসপেন্স একীভূত করা শুরু করবেন, তখন আপনি দেখতে পাবেন যে এটি কেবল আপনার অ্যাপ্লিকেশনের পারফরম্যান্স এবং ব্যবহারকারীর অভিজ্ঞতাই উন্নত করে না, বরং আপনার স্টেট ম্যানেজমেন্ট লজিককেও নাটকীয়ভাবে সরল করে, যা আপনাকে সত্যিই গুরুত্বপূর্ণ বিষয়গুলোতে ফোকাস করতে দেয়: দুর্দান্ত ফিচার তৈরি করা।